package com.why.rpc.core.common.serializable;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.extern.slf4j.Slf4j;
import com.why.rpc.core.common.enums.SerializerCode;
import com.why.rpc.core.common.message.RpcRequest;

import java.io.IOException;

/**
 * @program: whyrpcframework
 * @description:
 * @author: @why
 * @create: 2021-03-11 19:14
 **/
@Slf4j
public class JsonSerializer implements CommonSerializable {
    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 序列化
     *
     * @param object
     * @return
     */
    @Override
    public byte[] serialize(Object object) {

        try {
            return objectMapper.writeValueAsBytes(object);
        } catch (JsonProcessingException e) {
            log.error("序列化时发生错误！", e);
            throw new RuntimeException(e.getMessage());
        }


    }

    @Override
    public Object deserialize(byte[] bytes, Class<?> clazz) {
        try {

            Object value = objectMapper.readValue(bytes, clazz);
            if (value instanceof RpcRequest) {
                value = reuqesthandler(value);
            }

            return value;
        } catch (IOException e) {
            log.error("反序列化发生错误", e);
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * json的序列化和反序列化无法保证反序列化后仍然为原实例类型
     * 需要重新判断处理
     *
     * @param obj
     * @return
     */
    private Object reuqesthandler(Object obj) throws IOException {
        RpcRequest request = (RpcRequest) obj;

        log.info("处理前reuqesthandler:"+request);

        for (int i = 0; i < request.getParameterTypes().length; i++) {
            //获取当前对象中的参数类型
            /**
             *  Object 数组在反序列化时序列化器会根据字段类型进行反序列化，而 Object 就是一个十分模糊的类型，会出现反序列化失败的现象，
             *  控制台输出  {name=hello netty, id=381}，但是实际这个参数是HelloObject类型的，因此我们根据parameteTypes完成类型的处理
             */
            Class<?> clazz = request.getParameterTypes()[i];
//            isAssignableFrom()方法是判断是否为某个类的父类，instanceof关键字是判断是否某个类的子类。
//            父类.class.isAssignableFrom(子类.class)
            //如果当前的得到的类不是实际的类型，那就进行类型转换
            if (!clazz.isAssignableFrom(request.getMethodParameters()[i].getClass())) {
                //根据类型重新序列化一下  将当前的参数序列化
                byte[] result = objectMapper.writeValueAsBytes(request.getMethodParameters()[i]);
                //将字节流按照参数的类型重新变为真实的对象。
                request.getMethodParameters()[i] = objectMapper.readValue(result, clazz);

            }

        }
        log.info("处理后reuqesthandler:"+request);

        return request;
    }


    @Override
    public int getCode() {
        //该方法形参只要一个String类型的值即可，
        // 并且该String值为枚举值对应的名称，
        // 即toString()方法返回的值（比如枚举类为SPRING，对应的名称就是"SPRING"），所以肯定是需要双引号的；
        //就是通过字符串获取枚举类对象
        return SerializerCode.valueOf("JSON").getCode();
    }


}
